using System;
using System.Runtime.InteropServices;
using Lockstep.Math;
using Lockstep.Util;

namespace Lockstep.Collision2D
{
    [Serializable]
    public partial class CTransform2D : IComponent
    {
        /// <summary>
        /// posXZ
        /// </summary>
        public LVector2 pos;
        public LFloat y;
        /// <summary>
        /// 角度
        /// </summary>
        public LFloat deg; //same as Unity CW deg(up) =0

        /// <summary>
        ///  等同于2D  up
        /// </summary>
        [NoBackup]
        public LVector2 forward
        {
            get {
                LFloat s, c;
                var ccwDeg = (-deg + 90);
                LMath.SinCos(out s, out c, LMath.Deg2Rad * ccwDeg);
                return new LVector2(c, s);
            }
            set {

                LFloat s, c;
                var ccwDeg = (-deg + 90);
                LMath.SinCos(out s, out c, LMath.Deg2Rad * ccwDeg);
                deg = ToDeg(value);
            }
        }

        [NoBackup]
        public LVector3 Pos3
        {
            get {
                return new LVector3(pos.x, y, pos.y);
            }
            set {
                pos = new LVector2(value.x, value.z);
                y = value.y;
            }
        }

        /// <summary>
        /// value这个值在2D上转过的角度,且约束在[0,360]
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static LFloat ToDeg(LVector2 value)
        {
            var ccwDeg = LMath.Atan2(value.y, value.x) * LMath.Rad2Deg;
            var deg = 90 - ccwDeg;
            return AbsDeg(deg);
        }

        /// <summary>
        /// 传入目标点，进行转向
        /// </summary>
        /// <param name="targetPos"></param>
        /// <param name="currentPos"></param>
        /// <param name="cursDeg"></param>
        /// <param name="turnVal">一帧内的转向值</param>
        /// <param name="isLessDeg"></param>
        /// <returns></returns>
        public static LFloat TurnToward(LVector2 targetPos, LVector2 currentPos, LFloat cursDeg, LFloat turnVal, out bool isLessDeg)
        {
            var toTarget = (targetPos - currentPos).normalized;
            var toDeg = CTransform2D.ToDeg(toTarget);//目标角度
            return TurnToward(toDeg, cursDeg, turnVal, out isLessDeg);
        }

        public static LFloat TurnToward(LFloat toDeg, LFloat cursDeg, LFloat turnVal, out bool isLessDeg)
        {
            var curDeg = CTransform2D.AbsDeg(cursDeg);
            var diff = toDeg - curDeg;
            var absDiff = LMath.Abs(diff);
            isLessDeg = absDiff < turnVal;
            if (isLessDeg)//转向差异小于一帧的转向差异则直接返回目标转向
            {
                return toDeg;
            }
            else
            {
                if (absDiff > 180)
                {
                    if (diff > 0)//顺时针转
                    {
                        diff -= 360;//[0,-180]
                    }
                    else//逆时针转
                    {
                        diff += 360;//[0,180]
                    }
                }

                return curDeg + turnVal * LMath.Sign(diff);
            }
        }

        /// <summary>
        /// deg约束在[0,360]
        /// </summary>
        /// <param name="deg"></param>
        /// <returns></returns>
        public static LFloat AbsDeg(LFloat deg)
        {
            var rawVal = deg._val % ((LFloat)360)._val;
            return new LFloat(true, rawVal);
        }

        public CTransform2D() { }

        public CTransform2D(LVector2 pos, LFloat y) : this(pos, y, LFloat.zero) { }

        public CTransform2D(LVector2 pos) : this(pos, LFloat.zero, LFloat.zero) { }

        public CTransform2D(LVector2 pos, LFloat y, LFloat deg)
        {
            this.pos = pos;
            this.y = y;
            this.deg = deg;
        }

        public void Reset()
        {
            pos = LVector2.zero;
            y = LFloat.zero;
            deg = LFloat.zero;
        }

        /// <summary>
        /// 将point从局部坐标转为世界坐标
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public LVector2 TransformPoint(LVector2 point)
        {
            return pos + TransformDirection(point);
        }

        /// <summary>
        /// 将传入的向量从局部坐标系转为世界坐标系
        /// </summary>
        /// <param name="vec"></param>
        /// <returns></returns>
        public LVector2 TransformVector(LVector2 vec)
        {
            return TransformDirection(vec);
        }

        /// <summary>
        /// 将传入的方向从局部坐标系转为世界坐标系
        /// </summary>
        /// <param name="dir"></param>
        /// <returns></returns>
        public LVector2 TransformDirection(LVector2 dir)
        {
            var y = forward;
            var x = forward.RightVec();
            return dir.x * x + dir.y * y;//水平方向*sin，竖直方向乘cos
        }

        public static Transform2D operator +(CTransform2D a, CTransform2D b)
        {
            return new Transform2D { pos = a.pos + b.pos, y = a.y + b.y, deg = a.deg + b.deg };
        }

        public override string ToString()
        {
            return $"(deg:{deg} pos:{pos} y:{y})";
        }

        public void Log()
        {
            UnityEngine.Debug.Log($"[{ToString()}] [forward:{forward}] [Pos3:{Pos3}]");
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = NativeHelper.STRUCT_PACK)]
    public unsafe struct Transform2D
    {
        public LVector2 pos;
        public LFloat y;
        public LFloat deg;
    }
}